// Bitmap.cpp

#include "stdafx.h"
#include "Bitmap.h"

/** \class GDIFont
\brief Win32 GDI font wrapper
\ingroup ptGfx
*/


/// CTor, initialize to NULL
GDIFont::GDIFont()
{ 
	m_hFont=NULL;
	m_axKerning=NULL;
	m_iKerningCnt=0;
};

/// DTor, frees all resources.
GDIFont::~GDIFont()
{ 
	Free();
};

GDIFont::GDIFont(const GDIFont& p_xrA)
{
	m_hFont=p_xrA.m_hFont;
	m_iKerningCnt=p_xrA.m_iKerningCnt;
	m_axKerning=new KerningPair[m_iKerningCnt];
	memcpy(m_axKerning,p_xrA.m_axKerning,sizeof(KerningPair)*m_iKerningCnt);
};

GDIFont& GDIFont::operator=(const GDIFont& p_xrA)
{
	if(&p_xrA==this){return *this;};
	Free();
	m_hFont=p_xrA.m_hFont;
	m_iKerningCnt=p_xrA.m_iKerningCnt;
	m_axKerning=new KerningPair[m_iKerningCnt];
	memcpy(m_axKerning,p_xrA.m_axKerning,sizeof(KerningPair)*m_iKerningCnt);
	return *this;
};

/// Calculate the kerning in pixels between two unicode characters \p dwFirst and \p dwSecond.
int GDIFont::GetKerning(dword uFirst,dword uSecond) const
{
	if(m_iKerningCnt==0){return 0;};
	int i=m_iKerningCnt/2;
	int iStep = i;
	word wF=(word)(uFirst);
	word wS=(word)(uSecond);
	while ((m_axKerning[i].m_uFirst!=wF)&&(iStep!=0))
	{
		iStep/=2;
		if(m_axKerning[i].m_uFirst<wF) 
		{
			i+=iStep;
		}
		else
		{
			i-=iStep;
		};
	};


	if(m_axKerning[i].m_uFirst!=wF)
	{
		return 0;
	};

	while((m_axKerning[i].m_uFirst==wF)&&(i>=0))
	{
		--i;
	};
	if(i!=0){i++;};
	for(;i<m_iKerningCnt;++i)
	{
		if(m_axKerning[i].m_uSecond==wS)
		{
			return m_axKerning[i].m_iOffset;
		};
		if(m_axKerning[i].m_uFirst!=wF)
		{
			break;
		};
	};
	return 0;
};

/// Create a GDI font.
bool GDIFont::Init(const char* sFaceName,int iPixelHeight,int iWeight,int iCharSet)
{ 
	Free();
	m_hFont=::CreateFontA(iPixelHeight,0,0,0,iWeight,0,0,0,iCharSet,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,ANTIALIASED_QUALITY,DEFAULT_PITCH,sFaceName);
	if(m_hFont!=NULL)
	{
		HDC hDC=::CreateCompatibleDC(NULL);
		HFONT hOldFont=(HFONT)::SelectObject(hDC,m_hFont);
		m_iKerningCnt=::GetKerningPairs(hDC,0,NULL);
		if(m_iKerningCnt>0)
		{
			m_axKerning=new KerningPair[m_iKerningCnt];
			::GetKerningPairs(hDC,m_iKerningCnt,(KERNINGPAIR*)m_axKerning);
		};
		::SelectObject(hDC, hOldFont);
		::DeleteDC(hDC);
	};
	/*
	LOGFONT logfont;
	memset(&logfont, 0, sizeof(logfont));
	logfont.lfHeight		= -iPixelHeight;
	logfont.lfWeight = FW_NORMAL;
	//	logfont.lfCharSet=RUSSIAN_CHARSET;
	logfont.lfCharSet=HANGEUL_CHARSET;
	logfont.lfOutPrecision	= OUT_TT_ONLY_PRECIS;
	logfont.lfQuality		= ANTIALIASED_QUALITY; 
	logfont.lfPitchAndFamily = VARIABLE_PITCH;
	strncpy(logfont.lfFaceName, sFaceName, LF_FACESIZE); 
	m_hFont = CreateFontIndirect(&logfont);
	*/
	return m_hFont!=NULL;
};

/// Free all resources.
void GDIFont::Free()
{ 
	if(m_hFont)
	{
		::DeleteObject(m_hFont);
		m_hFont=NULL;
	};
	delete [] m_axKerning;
	m_iKerningCnt=0;
};

class TextBitmapper
{
public:
	Bitmap	xBM;

	void GetTextExtent(const char* sText,void* m_hFont,SIZE& xrSize)
	{ 
		HFONT hOld,hCur;
		HDC hDC=::CreateCompatibleDC(NULL);
		//	::SelectObject(hDC,hDIB);
		::SetBkMode(hDC,TRANSPARENT);
		//	::SetTextColor(hDC,dwColor);
		if(m_hFont)
		{
			hCur=(HFONT)(m_hFont);
			hOld=(HFONT)::SelectObject(hDC,hCur);
			::GetTextExtentPoint32A(hDC, sText, (int)strlen(sText), &xrSize); 
			::SelectObject(hDC,hOld);
		}
		else
		{
			::GetTextExtentPoint32A(hDC, sText, (int)strlen(sText), &xrSize); 
		};
		::DeleteDC(hDC);
	};

	void DrawText(int iX,int iY,const char* sText,void* m_hFont)
	{ 
		HFONT hOld=0,hCur=0;
		HDC hDC=::CreateCompatibleDC(NULL);
		::SelectObject(hDC,xBM.m_hDIB);
		::SetBkMode(hDC,TRANSPARENT);
		::SetTextColor(hDC,0xffffff);
		if(m_hFont)
		{
			hCur=(HFONT)(m_hFont);
			hOld=(HFONT)::SelectObject(hDC,hCur);
		};
		::TextOutA(hDC,iX,iY,sText,(int)strlen(sText));
		if(m_hFont)
		{
			::SelectObject(hDC,hOld);
		};
		::DeleteDC(hDC);
	};


	void FillText(Bitmap& xrBM,const char* sText,int iX,int iY,void* m_hFont,dword dwColor)
	{ 
		if((sText==NULL)||(AStr::StrLen(sText)>65536)){return;};
		SIZE xSz;
		GetTextExtent(sText,m_hFont,xSz);
		if((xSz.cx<1)||(xSz.cy<1)||(xSz.cx>2048)||(xSz.cy>2048)){return;};
		if((xBM.m_iW<xSz.cx)||(xBM.m_iH<xSz.cy))
		{
			xBM.Init(xSz.cx,xSz.cy);
		};
		if(!xBM.m_pBM){return;};
		xBM.Clear(0);
		DrawText(0,0,sText,m_hFont);
		int x,y;
		for(y=0;y<xSz.cy;y++)
		{
			dword* pdwS=&(xBM.m_pBM[(y*xBM.m_iW)]);
			if((y+iY>=0)&&(y+iY<xrBM.m_iH))
			{
				dword* pdwD=&(xrBM.m_pBM[((y+iY)*xrBM.m_iW)]);
				for(x=0;x<xSz.cx;x++)
				{
					if((x+iX>0)&&(x+iX<xrBM.m_iW))
					{
						dword dwS=pdwS[x];
						if(dwS!=0)
						{
							int iA=dwS&0xff;
							if(iA<0xff)
							{
								iA=(iA*iA)>>8;
								dword dwD=pdwD[x+iX];
								dword iSRB=dwColor&0xff00ff;
								dword iSAG=(dwColor>>8)&0xff00ff;
								dword iDRB=dwD&0xff00ff;
								dword iDAG=(dwD>>8)&0xff00ff;
								int iInvA=256-iA;
								iSRB=((iSRB*iA+iDRB*iInvA)>>8)&0xff00ff;
								iSAG=((iSAG*iA+iDAG*iInvA)>>8)&0xff00ff;
								pdwD[x+iX]=iSRB|(iSAG<<8);
								//								pdwD[x+iX]=dwS;
							}
							else
							{
								pdwD[x+iX]=dwColor;
							};
						};
					};
				};
			};
		};
	};

	void FillTextPlain(Bitmap& xrBM,const char* sText,int iX,int iY,void* m_hFont,dword dwColor)
	{ 
		SIZE xSz;
		GetTextExtent(sText,m_hFont,xSz);
		if((xBM.m_iW<xSz.cx)||(xBM.m_iH<xSz.cy))
		{
			xBM.Init(xSz.cx,xSz.cy);
		};
		xBM.Clear(0);
		DrawText(0,0,sText,m_hFont);
		int x,y;
		for(y=0;y<xSz.cy;y++)
		{
			dword* pdwS=&(xBM.m_pBM[(y*xBM.m_iW)]);
			if((y+iY>=0)&&(y+iY<xrBM.m_iH))
			{
				dword* pdwD=&(xrBM.m_pBM[((y+iY)*xrBM.m_iW)]);
				for(x=0;x<xSz.cx;x++)
				{
					if((x+iX>0)&&(x+iX<xrBM.m_iW))
					{
						if(pdwS[x]!=0)
						{
							pdwD[x+iX]=dwColor;
						};
					};
				};
			};
		};
	};

};




Bitmap::Bitmap()
{
	m_hDIB=NULL;
	m_pBM=NULL;
	m_iW=0;
	m_iH=0;
};

Bitmap::~Bitmap()
{
	Shut();
};

void Bitmap::Shut()
{
	if(m_pBM)
	{
		::DeleteObject(m_hDIB);
		m_hDIB=NULL;
		m_pBM=NULL;
		m_iW=0;
		m_iH=0;
	};
};


void Bitmap::Init(int p_iWidth,int p_iHeight)
{
	Shut();

	BITMAPINFO xBI;
	xBI.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
	xBI.bmiHeader.biWidth=p_iWidth;
	xBI.bmiHeader.biHeight=-p_iHeight;
	xBI.bmiHeader.biPlanes=1;
	xBI.bmiHeader.biBitCount=32;
	xBI.bmiHeader.biCompression=BI_RGB;
	xBI.bmiHeader.biSizeImage=0;
	HDC hScreenDC=::CreateCompatibleDC(NULL);
	m_hDIB=::CreateDIBSection(hScreenDC,&xBI,DIB_RGB_COLORS,
		(void**)&m_pBM,NULL,NULL);
	::DeleteDC(hScreenDC);
	if(m_pBM)
	{
		m_iW=p_iWidth;
		m_iH=p_iHeight;
	};
};

void Bitmap::Clear(DWORD p_dwColor)
{
	int i,iC=m_iW*m_iH;
	for(i=0;i<iC;i++)
	{
		m_pBM[i]=p_dwColor;
	};
};

void Bitmap::SetPixel(int p_iX,int p_iY,DWORD p_dwColor)
{
	if((p_iX>=0)&&(p_iY>=0)&&(p_iX<m_iW)&&(p_iY<m_iH))
	{
		m_pBM[p_iX+p_iY*m_iW]=p_dwColor;
	};
};

void Bitmap::ColorUnpack(DWORD p_dwColor,int& po_iR,int& po_iG,int& po_iB)
{
	po_iR=(p_dwColor>>16)&0xff;
	po_iG=(p_dwColor>>8)&0xff;
	po_iB=(p_dwColor)&0xff;
};

DWORD Bitmap::ColorPack(int p_iR,int p_iG,int p_iB)
{
	int iR=Clamp(p_iR,0,255);
	int iG=Clamp(p_iG,0,255);
	int iB=Clamp(p_iB,0,255);
	return (iR<<16)|(iG<<8)|(iB);
};

namespace {
	void CalcShift(dword dwMaskIn,dword dwMaskOut,int& irShiftRight,dword& dwrMask,int& irShiftLeft)
	{
		irShiftRight=0;dwrMask=0;irShiftLeft=0;
		if((dwMaskIn==0)||(dwMaskOut==0)){return;};
		if(dwMaskOut==dwMaskIn){dwrMask=dwMaskOut;return;};
		dword dwM;
		long lSi /* shift */,lWi /* width */;
		long lSo /* shift */,lWo /* width */;
		dwM=dwMaskIn;
		lSi=0;lWi=0;
		while((dwM&1)==0){lSi++;dwM=dwM>>1;};
		dwrMask=dwM;
		while((dwM&1)!=0){lWi++;dwM=dwM>>1;};
		dwM=dwMaskOut;
		lSo=0;lWo=0;
		while((dwM&1)==0){lSo++;dwM=dwM>>1;};
		dwrMask&=dwM;
		while((dwM&1)!=0){lWo++;dwM=dwM>>1;};
		irShiftRight=lSi;
		if(lWi>lWo){irShiftRight+=lWi-lWo;};
		irShiftLeft=lSo;
		if(lWo>lWi){irShiftLeft+=lWo-lWi;};
	};
};

void Bitmap::PixelFormatConversion(void* pSrc,dword dwSR,dword dwSG,dword dwSB,dword dwSA,void* pDst,dword dwDR,dword dwDG,dword dwDB,dword dwDA,int iNumPixels)
{ 
	byte* pNMem=(byte*)pDst;
	int iDD,iSD;

	iSD=2;
	if(((dwSR|dwSG|dwSB|dwSA)&0x800000)!=0){iSD=3;};
	if(((dwSR|dwSG|dwSB|dwSA)&0x80000000)!=0){iSD=4;};
	iDD=2;
	if(((dwDR|dwDG|dwDB|dwDA)&0x800000)!=0){iDD=3;};
	if(((dwDR|dwDG|dwDB|dwDA)&0x80000000)!=0){iDD=4;};

	int iSLr,iSLg,iSLb,iSLa;
	int iSRr,iSRg,iSRb,iSRa;
	dword dwSMr,dwSMg,dwSMb,dwSMa;

	CalcShift(dwSR,dwDR,iSRr,dwSMr,iSLr);
	CalcShift(dwSG,dwDG,iSRg,dwSMg,iSLg);
	CalcShift(dwSB,dwDB,iSRb,dwSMb,iSLb);
	CalcShift(dwSA,dwDA,iSRa,dwSMa,iSLa);

	memset(pDst,0,iDD*iNumPixels);
	byte*	pBs;
	byte*	pBd;
	dword	dwS;
	dword	dwD;

	pBd=(byte*)pDst;pBs=(byte*)pSrc;
	int iP;
	for(iP=0;iP<iNumPixels-1;iP++)
	{
		dwS=*((dword*)pBs);
		dwD=*((dword*)pBd);
		dwD=dwD|(((dwS>>iSRr)&dwSMr)<<iSLr);
		dwD=dwD|(((dwS>>iSRg)&dwSMg)<<iSLg);
		dwD=dwD|(((dwS>>iSRb)&dwSMb)<<iSLb);
		dwD=dwD|(((dwS>>iSRa)&dwSMa)<<iSLa);
		*((dword*)pBd)=dwD;
		pBs+=iSD;
		pBd+=iDD;
	};
	// last pixel
	dwS=*((dword*)(&(((byte*)pSrc)[(iNumPixels-1)*iSD])));
	dwD=(((dwS>>iSRr)&dwSMr)<<iSLr);
	dwD|=(((dwS>>iSRg)&dwSMg)<<iSLg);
	dwD|=(((dwS>>iSRb)&dwSMb)<<iSLb);
	dwD|=(((dwS>>iSRa)&dwSMa)<<iSLa);
	for (int i=0;i<iDD;i++)
	{
		pNMem[(iNumPixels-1)*iDD+i]=(byte)(dwD&0x0FF);
		dwD=dwD>>8;
	}
};


bool Bitmap::LoadTGA(const char* p_sFilename)
{ 
	FILE* pF=fopen(p_sFilename,"rb");
	if(!pF){return false;};

	int iWid,iHei,iLen,v,iDep,iCnt,x,y,iSrc;
	bool bUpsideDown;
	bool bPacked;
	byte r,g,b;
	int iIdentLen;
	int iCMapLen=0;
	iIdentLen=((byte)(fgetc(pF)));
	r=((byte)(fgetc(pF)));
	if(r==0){iCMapLen=0;};
	if(r==1){iCMapLen=1;};
	if(r>1){fclose(pF);return false;};
	r=((byte)(fgetc(pF)));
	if((r!=2)&&(r!=10)){fclose(pF);return false;};
	bPacked=false;
	if(r==10){bPacked=true;};
	v=((byte)(fgetc(pF)));v=v+((byte)(fgetc(pF)))*256;	// first palette entry
	v=((byte)(fgetc(pF)));v=v+((byte)(fgetc(pF)))*256;	// number of palette entries
	r=((byte)(fgetc(pF)));
	if(iCMapLen==1)
	{
		iCMapLen=v*(r)/8;
	};
	v=((byte)(fgetc(pF)));v=v+((byte)(fgetc(pF)))*256;	// origin-x
	v=((byte)(fgetc(pF)));v=v+((byte)(fgetc(pF)))*256;	// origin-y
	v=((byte)(fgetc(pF)));v=v+((byte)(fgetc(pF)))*256;	// width;
	iWid=v;
	v=((byte)(fgetc(pF)));v=v+((byte)(fgetc(pF)))*256;	// height
	iHei=v;
	iDep=((byte)(fgetc(pF)));
	r=((byte)(fgetc(pF)));	// direction
	int iACnt=r&15;
	bUpsideDown=true;
	if(r&32){bUpsideDown=false;};
	// test validity
	if(iWid<0){fclose(pF);return false;};
	if(iHei<0){fclose(pF);return false;};
	if((iDep!=16)&&(iDep!=24)&&(iDep!=32)){fclose(pF);return false;};
	iLen=iWid*iHei;
	int iDepth=3;
	int iBytes=3*iLen;
	dword dwRMask=0,dwGMask=0,dwBMask=0,dwAMask=0;
	if(iDep==32)
	{
		iBytes=4*iLen;iDepth=4;
		dwRMask=0xff0000;dwGMask=0xff00;dwBMask=0xff;
		if(iACnt){
			dwAMask=0xff000000;
		}
		else
		{
			dwAMask=0x0;
		};
	};
	if(iDep==24)
	{
		iBytes=3*iLen;iDepth=3;
		dwRMask=0xff0000;dwGMask=0xff00;dwBMask=0xff;
		dwAMask=0x0;
	};
	if(iDep==16)
	{
		iBytes=2*iLen;iDepth=2;
		dwRMask=0x7c00;dwGMask=0x3e0;dwBMask=0x1f;
		dwAMask=0x0;
	};

	int iAllocSize=Max(iBytes,iIdentLen);
	iAllocSize=Max(iAllocSize,iCMapLen);

	byte* pMem=new byte[iAllocSize+8];

	// skip identification (if any)
	if(iIdentLen)
	{
		fread(pMem,iIdentLen,1,pF);
	};
	// skip cmap (if any)
	if(iCMapLen)
	{
		fread(pMem,iCMapLen,1,pF);
	};

	byte* pSrc,*pSrcO;
	pSrcO=pSrc=(byte*)(new byte[iBytes+256]);

	if(bPacked)
	{
		if(iDep==16){
			iSrc=0;
			while(iSrc<iBytes){
				b=((byte)(fgetc(pF)));v=b;
				if(v&0x80){
					v=v&0x7f;
					b=((byte)(fgetc(pF)));g=((byte)(fgetc(pF)));
					for(x=0;x<v+1;x++){
						pSrc[iSrc++]=b;pSrc[iSrc++]=g;
					};
				}else{
					for(x=0;x<v+1;x++){
						b=((byte)(fgetc(pF)));g=((byte)(fgetc(pF)));
						pSrc[iSrc++]=b;pSrc[iSrc++]=g;
					};
				};
			};
		};
		if(iDep==24){
			iSrc=0;
			while(iSrc<iBytes){
				b=((byte)(fgetc(pF)));v=b;
				if(v&0x80){
					v=v&0x7f;
					b=((byte)(fgetc(pF)));g=((byte)(fgetc(pF)));r=((byte)(fgetc(pF)));
					for(x=0;x<v+1;x++){
						pSrc[iSrc++]=b;pSrc[iSrc++]=g;pSrc[iSrc++]=r;
					};
				}else{
					for(x=0;x<v+1;x++){
						b=((byte)(fgetc(pF)));g=((byte)(fgetc(pF)));r=((byte)(fgetc(pF)));
						pSrc[iSrc++]=b;pSrc[iSrc++]=g;pSrc[iSrc++]=r;
					};
				};
			};
		};
		if(iDep==32){
			iSrc=0;
			while(iSrc<iBytes){
				b=((byte)(fgetc(pF)));v=b;
				if(v&0x80){
					v=v&0x7f;
					b=((byte)(fgetc(pF)));g=((byte)(fgetc(pF)));r=((byte)(fgetc(pF)));y=((byte)(fgetc(pF)));
					for(x=0;x<v+1;x++){
						pSrc[iSrc++]=b;pSrc[iSrc++]=g;pSrc[iSrc++]=r;pSrc[iSrc++]=(byte)y;
					};
				}else{
					for(x=0;x<v+1;x++){
						b=((byte)(fgetc(pF)));g=((byte)(fgetc(pF)));r=((byte)(fgetc(pF)));y=((byte)(fgetc(pF)));
						pSrc[iSrc++]=b;pSrc[iSrc++]=g;pSrc[iSrc++]=r;pSrc[iSrc++]=(byte)y;
					};
				};
			};
		};
	}
	else
	{
		fread(pSrc,iBytes,1,pF);
	};

	iSrc=0;
	if(bUpsideDown)
	{
		if(iDep==16)
		{
			for(y=iHei-1;y>=0;y--)
			{
				iCnt=y*iWid*2;
				for(x=0;x<iWid;x++)
				{
					b=*(pSrc++);g=*(pSrc++);
					pMem[iCnt++]=b;pMem[iCnt++]=g;
				};
			};
		};
		if(iDep==24)
		{
			for(y=iHei-1;y>=0;y--)
			{
				iCnt=y*iWid*3;
				for(x=0;x<iWid;x++)
				{
					b=*(pSrc++);g=*(pSrc++);r=*(pSrc++);
					pMem[iCnt++]=b;pMem[iCnt++]=g;pMem[iCnt++]=r;
				};
			};
		};
		if(iDep==32)
		{
			for(y=iHei-1;y>=0;y--)
			{
				iCnt=y*iWid*4;
				for(x=0;x<iWid;x++)
				{
					b=*(pSrc++);g=*(pSrc++);r=*(pSrc++);v=*(pSrc++);
					pMem[iCnt++]=b;pMem[iCnt++]=g;pMem[iCnt++]=r;pMem[iCnt++]=(byte)v;
				};
			};
		};
	}
	else
	{
		memcpy(pMem,pSrc,iBytes);
	};

	delete [] pSrcO;

	Init(iWid,iHei);
	PixelFormatConversion(pMem,dwRMask,dwGMask,dwBMask,dwAMask,m_pBM,0xff0000,0xff00,0xff,0xff000000,iLen);
	delete [] pMem;
	fclose(pF);
	return true;

};

bool Bitmap::SaveTGA(const char* p_sFilename)
{
	FILE* pF=fopen(p_sFilename,"wb");
	if(!pF){return false;};
	fputc(0,pF);
	fputc(0,pF);
	fputc(2,pF);
	fputc(0,pF);fputc(0,pF);
	fputc(0,pF);fputc(0,pF);
	fputc(0,pF);
	fputc(0,pF);fputc(0,pF);
	fputc(0,pF);fputc(0,pF);
	fputc((byte)((m_iW)&0xff),pF);fputc((byte)((m_iW>>8)&0xff),pF);
	fputc((byte)((m_iH)&0xff),pF);fputc((byte)((m_iH>>8)&0xff),pF);
	//	fputc(0);
	fputc(32,pF);
	fputc(8|32,pF);
	fwrite(m_pBM,m_iW*m_iH*4,1,pF);
	fclose(pF);
	return true;
};

void Bitmap::DrawLine(int p_iX0,int p_iY0,int p_iX1,int p_iY1,DWORD p_dwColor)
{
	int iDX=p_iX1-p_iX0;
	int iDY=p_iY1-p_iY0;
	if((iDX==0)&&(iDY==0))
	{
		SetPixel(p_iX0,p_iY0,p_dwColor);
		return;
	};
	int i,iSteps;
	if(abs(iDX)>=abs(iDY))
	{
		iSteps=abs(iDX);
	}
	else
	{
		iSteps=abs(iDY);
	};
	for(i=0;i<=iSteps;i++)
	{
		int iX=p_iX0+(iDX*i)/iSteps;
		int iY=p_iY0+(iDY*i)/iSteps;
		SetPixel(iX,iY,p_dwColor);
	};
};

void Bitmap::DrawLineIpol(int p_iX0,int p_iY0,DWORD p_dwColor0,int p_iX1,int p_iY1,DWORD p_dwColor1)
{
	int iDX=p_iX1-p_iX0;
	int iDY=p_iY1-p_iY0;
	int iR0,iG0,iB0;
	int iR1,iG1,iB1;
	ColorUnpack(p_dwColor0,iR0,iG0,iB0);
	ColorUnpack(p_dwColor1,iR1,iG1,iB1);
	int iDR=iR1-iR0;
	int iDG=iG1-iG0;
	int iDB=iB1-iB0;
	if((iDX==0)&&(iDY==0))
	{
		SetPixel(p_iX0,p_iY0,p_dwColor0);
		return;
	};
	int i,iSteps;
	if(abs(iDX)>=abs(iDY))
	{
		iSteps=abs(iDX);
	}
	else
	{
		iSteps=abs(iDY);
	};
	for(i=0;i<=iSteps;i++)
	{
		int iX=p_iX0+(iDX*i)/iSteps;
		int iY=p_iY0+(iDY*i)/iSteps;
		int iR=iR0+(iDR*i)/iSteps;
		int iG=iG0+(iDG*i)/iSteps;
		int iB=iB0+(iDB*i)/iSteps;
		SetPixel(iX,iY,ColorPack(iR,iG,iB));
	};
};

bool Bitmap::DrawTriEdge(
				 int p_iX0,int p_iY0,DWORD p_dwColor0,
				 int p_iX1,int p_iY1,DWORD p_dwColor1,
				 int p_iX2,int p_iY2,DWORD p_dwColor2
				 )
{
	DrawLineIpol(p_iX0,p_iY0,p_dwColor0,p_iX1,p_iY1,p_dwColor1);
	DrawLineIpol(p_iX0,p_iY0,p_dwColor0,p_iX2,p_iY2,p_dwColor2);
	DrawLineIpol(p_iX2,p_iY2,p_dwColor2,p_iX1,p_iY1,p_dwColor1);
	return true;
};

void Bitmap::DrawCircle(int p_iX0,int p_iY0,int p_iR,DWORD p_dwColor)
{
	int iT;
	int iR2=p_iR*p_iR+p_iR/2;
	int iTMax=p_iR*46/64;	// ~0.707
	for(iT=0;iT<=iTMax;iT++)
	{
		int iU=int(sqrt((float)(iR2-iT*iT)));
		SetPixel(p_iX0+iT,p_iY0-iU,p_dwColor);
		SetPixel(p_iX0-iT,p_iY0-iU,p_dwColor);
		SetPixel(p_iX0-iU,p_iY0-iT,p_dwColor);
		SetPixel(p_iX0+iU,p_iY0-iT,p_dwColor);
		SetPixel(p_iX0+iT,p_iY0+iU,p_dwColor);
		SetPixel(p_iX0-iT,p_iY0+iU,p_dwColor);
		SetPixel(p_iX0-iU,p_iY0+iT,p_dwColor);
		SetPixel(p_iX0+iU,p_iY0+iT,p_dwColor);
	};
};

void Bitmap::FillCircle(int p_iX0,int p_iY0,int p_iR,DWORD p_dwColor)
{
	int iT;
	int iR2=p_iR*p_iR+p_iR/2;
	for(iT=-p_iR;iT<=p_iR;iT++)
	{
		int iU=int(sqrt((float)(iR2-iT*iT)));
		DrawLine(p_iX0-iU,p_iY0+iT,p_iX0+iU,p_iY0+iT,p_dwColor);
	};
};

void Bitmap::DrawSpan(int p_iX0,int p_iX1,int p_iY,DWORD p_dwColor0,DWORD p_dwColor1)
{
	if((p_iY<0)||(p_iY>=m_iH))
	{
		return;
	};

	if(p_iX1<p_iX0)
	{
		Swap(p_iX0,p_iX1);
		Swap(p_dwColor0,p_dwColor1);
	};

	if(p_iX0==p_iX1)
	{
		SetPixel(p_iX0,p_iY,p_dwColor0);
		return;
	};

	if(p_iX1<0){return;};
	if(p_iX0>=m_iW){return;};

	int iRbase,iGbase,iBbase;
	int iRdist,iGdist,iBdist;

	ColorUnpack(p_dwColor0,iRbase,iGbase,iBbase);
	ColorUnpack(p_dwColor1,iRdist,iGdist,iBdist);
	iRdist-=iRbase;iGdist-=iGbase;iBdist-=iBbase;

	if(p_iX0<0)
	{
		int iT;
		iT=(iRdist*(-p_iX0))/(p_iX1-p_iX0);
		iRbase+=iT;iRdist-=iT;
		iT=(iGdist*(-p_iX0))/(p_iX1-p_iX0);
		iGbase+=iT;iGdist-=iT;
		iT=(iBdist*(-p_iX0))/(p_iX1-p_iX0);
		iBbase+=iT;iBdist-=iT;
		p_iX0=0;
	};
	if(p_iX1>=m_iW)
	{
		iRdist=(iRdist*(m_iW-1-p_iX0))/(p_iX1-p_iX0);
		iGdist=(iGdist*(m_iW-1-p_iX0))/(p_iX1-p_iX0);
		iBdist=(iBdist*(m_iW-1-p_iX0))/(p_iX1-p_iX0);
		p_iX1=m_iW-1;
	};
	if(p_iX0==p_iX1)
	{
		SetPixel(p_iX0,p_iY,p_dwColor0);
		return;
	};

	int iXRange=p_iX1-p_iX0;
	DWORD* pdwDest=&(m_pBM[p_iY*m_iW+p_iX0]);
	iRdist=(iRdist<<15)/iXRange;
	iGdist=(iGdist<<15)/iXRange;
	iBdist=(iBdist<<15)/iXRange;
	iRbase<<=15;
	iGbase<<=15;
	iBbase<<=15;
	for(int iX=0;iX<=iXRange;iX++)
	{
		int iR=iRbase>>15;
		int iG=iGbase>>15;
		int iB=iBbase>>15;
		pdwDest[iX]=(iR<<16)|(iG<<8)|(iB);
		iRbase+=iRdist;
		iGbase+=iGdist;
		iBbase+=iBdist;
	};
};

bool Bitmap::DrawTri(int p_iX0,int p_iY0,DWORD p_dwColor0,int p_iX1,int p_iY1,DWORD p_dwColor1,int p_iX2,int p_iY2,DWORD p_dwColor2)
{

	// cohen-sutherland test
	int iMask=0xf;
	if(p_iX0>=0){iMask&=~1;};
	if(p_iX1>=0){iMask&=~1;};
	if(p_iX2>=0){iMask&=~1;};
	if(p_iX0<m_iW){iMask&=~4;};
	if(p_iX1<m_iW){iMask&=~4;};
	if(p_iX2<m_iW){iMask&=~4;};
	if(p_iY0>=0){iMask&=~2;};
	if(p_iY1>=0){iMask&=~2;};
	if(p_iY2>=0){iMask&=~2;};
	if(p_iY0<m_iH){iMask&=~8;};
	if(p_iY1<m_iH){iMask&=~8;};
	if(p_iY2<m_iH){iMask&=~8;};
	if(iMask!=0){return false;};

	if(p_iY0>p_iY1)
	{
		Swap(p_iY0,p_iY1);
		Swap(p_iX0,p_iX1);
		Swap(p_dwColor0,p_dwColor1);
	};
	if(p_iY1>p_iY2)
	{
		Swap(p_iY1,p_iY2);
		Swap(p_iX1,p_iX2);
		Swap(p_dwColor1,p_dwColor2);
	};
	if(p_iY0>p_iY1)
	{
		Swap(p_iY0,p_iY1);
		Swap(p_iX0,p_iX1);
		Swap(p_dwColor0,p_dwColor1);
	};

	int iR0,iG0,iB0;
	int iR1,iG1,iB1;
	int iR2,iG2,iB2;
	ColorUnpack(p_dwColor0,iR0,iG0,iB0);
	ColorUnpack(p_dwColor1,iR1,iG1,iB1);
	ColorUnpack(p_dwColor2,iR2,iG2,iB2);


	if(p_iY0==p_iY1)
	{
		DrawSpan(p_iX0,p_iX1,p_iY0,p_dwColor0,p_dwColor1);
	}
	else
	{
		int iXad=((p_iX1-p_iX0)<<15)/(p_iY1-p_iY0);
		int iRad=((iR1-iR0)<<15)/(p_iY1-p_iY0);
		int iGad=((iG1-iG0)<<15)/(p_iY1-p_iY0);
		int iBad=((iB1-iB0)<<15)/(p_iY1-p_iY0);
		int iXbd=((p_iX2-p_iX0)<<15)/(p_iY2-p_iY0);
		int iRbd=((iR2-iR0)<<15)/(p_iY2-p_iY0);
		int iGbd=((iG2-iG0)<<15)/(p_iY2-p_iY0);
		int iBbd=((iB2-iB0)<<15)/(p_iY2-p_iY0);

		int iY0=Clamp(p_iY0,0,m_iH);
		int iY1=Clamp(p_iY1,iY0,m_iH);
		for(int iY=iY0;iY<iY1;iY++)
		{
			int iXa=p_iX0+(((iY-p_iY0)*iXad)>>15);
			int iXb=p_iX0+(((iY-p_iY0)*iXbd)>>15);
			int iRa=iR0+(((iY-p_iY0)*iRad)>>15);
			int iGa=iG0+(((iY-p_iY0)*iGad)>>15);
			int iBa=iB0+(((iY-p_iY0)*iBad)>>15);
			int iRb=iR0+(((iY-p_iY0)*iRbd)>>15);
			int iGb=iG0+(((iY-p_iY0)*iGbd)>>15);
			int iBb=iB0+(((iY-p_iY0)*iBbd)>>15);
			DrawSpan(iXa,iXb,iY,(iRa<<16)|(iGa<<8)|(iBa),(iRb<<16)|(iGb<<8)|(iBb));
		};
	};

	if(p_iY1==p_iY2)
	{
		DrawSpan(p_iX1,p_iX2,p_iY1,p_dwColor1,p_dwColor2);
	}
	else
	{
		int iXad=((p_iX2-p_iX1)<<15)/(p_iY2-p_iY1);
		int iRad=((iR2-iR1)<<15)/(p_iY2-p_iY1);
		int iGad=((iG2-iG1)<<15)/(p_iY2-p_iY1);
		int iBad=((iB2-iB1)<<15)/(p_iY2-p_iY1);
		int iXbd=((p_iX2-p_iX0)<<15)/(p_iY2-p_iY0);
		int iRbd=((iR2-iR0)<<15)/(p_iY2-p_iY0);
		int iGbd=((iG2-iG0)<<15)/(p_iY2-p_iY0);
		int iBbd=((iB2-iB0)<<15)/(p_iY2-p_iY0);

		int iY1=Clamp(p_iY1,0,m_iH);
		int iY2=Clamp(p_iY2,iY1,m_iH);
		for(int iY=iY1;iY<=iY2;iY++)
		{
			int iXa=p_iX1+(((iY-p_iY1)*iXad)>>15);
			int iXb=p_iX0+(((iY-p_iY0)*iXbd)>>15);
			int iRa=iR1+(((iY-p_iY1)*iRad)>>15);
			int iGa=iG1+(((iY-p_iY1)*iGad)>>15);
			int iBa=iB1+(((iY-p_iY1)*iBad)>>15);
			int iRb=iR0+(((iY-p_iY0)*iRbd)>>15);
			int iGb=iG0+(((iY-p_iY0)*iGbd)>>15);
			int iBb=iB0+(((iY-p_iY0)*iBbd)>>15);
			DrawSpan(iXa,iXb,iY,(iRa<<16)|(iGa<<8)|(iBa),(iRb<<16)|(iGb<<8)|(iBb));
		};
	};
	return true;
};

/**
\brief Output text with standard system font
\param p_iX top-left X coordinate
\param p_iY top-left Y coordinate
\param p_sText zero-terminated text string
\param p_dwColor text color
*/
void Bitmap::DrawTextA(int p_iX,int p_iY,const char* p_sText,dword p_dwColor)
{ 
	TextBitmapper xTBM;
	xTBM.FillText(*this,p_sText,p_iX,p_iY,NULL,p_dwColor);
};

/**
\brief Output text with given font
\param p_iX top-left X coordinate
\param p_iY top-left Y coordinate
\param p_sText zero-terminated text string
\param p_dwColor text color
\param p_rxFont font to use
*/

void Bitmap::DrawTextA(int p_iX,int p_iY,const char* p_sText,dword p_dwColor,const GDIFont& p_rxFont)
{ 
	TextBitmapper xTBM;
	xTBM.FillText(*this,p_sText,p_iX,p_iY,p_rxFont.m_hFont,p_dwColor);
};











